package middleware

import (
	"context"
	"net/http"
	"time"

	"gitee.com/shoyer/framework/ctx"
	"gitee.com/shoyer/framework/ctx/session"
	"gitee.com/shoyer/framework/keys"
	"gitee.com/shoyer/logger"
	"gitee.com/shoyer/service/rdb"
	"gitee.com/shoyer/util"
)

func GetCtx(r *http.Request) *ctx.Ctx {
	return r.Context().Value("_ctx_").(*ctx.Ctx)
}

func CreateCtx() Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			contx := &ctx.Ctx{Context: r.Context()}
			r1 := context.WithValue(contx, "_ctx_", contx)
			next.ServeHTTP(w, r.WithContext(r1))
		})
	}
}

func BuildCtx() Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			header := w.Header()
			header["Date"] = nil
			header["Content-Type"] = nil

			contx := GetCtx(r)
			params := r.URL.Query()
			contx.SetLocale(params.Get("locale"))
			contx.SetIP(util.GetIP(r))
			contx.SetParams(params)

			renderType := params.Get("r")
			if renderType == "" {
				renderType = r.Header.Get("r")
			}

			if renderType != "" {
				contx.SetRenderType(util.StringToUint(renderType))
			}

			next.ServeHTTP(w, r)
		})
	}
}

// !! Logging 应该是第一个被加载的中间件，在这个中间件中创建ctx.Ctx上下文，后面的逻辑都依赖这个。
func Logging() Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			start := time.Now().UnixNano()
			logger.Debugf("<------------------------------- %s", r.RequestURI)
			defer func() {
				// time used.
				end := time.Now().UnixNano()
				ms := (end - start) / 1000000
				ctx := GetCtx(r)
				session := ctx.GetSession()
				logger.Debugf("------------------------------->%s | %s=%s | %s=%dms | %s=%d | %s=%s \n", r.RequestURI, logger.Cyan("ip"), ctx.GetIP(), logger.Blue("rt"), ms, logger.Blue("uid"), session.UID, logger.Blue("sid"), session.ID)
			}()

			// * 创建ctx, 后面所有的中件，和逻辑都要依赖这个ctx.
			contx := &ctx.Ctx{Context: r.Context()}
			r1 := context.WithValue(contx, "_ctx_", contx)
			r = r.WithContext(r1) // * 必须用新的设置了ctx的r1覆盖掉原来r, 否则在上面的derfer中，getCtx还是从原来的r中获取不到ctx.Ctx
			next.ServeHTTP(w, r)
		})
	}
}

func Sessio() Middleware {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := GetCtx(r)

			defer func() {
				sion := ctx.GetSession()
				if sion != nil && sion.ID != "" && sion.UID > 0 {
					// 更新在线状态， 5分钟没有调用则认为离线
					rdb.GetDB().Set(rdb.GetKey(keys.Online, sion.UID), sion.ID, time.Minute*5)
				}
			}()

			s, _ := r.Cookie("s")
			sion := session.NewSession()

			if s == nil {
				sion = session.CreateHttpSession(w)
			} else {
				sion = session.GetSessionID(s.Value)
			}

			if sion != nil {
				ctx.SetSession(*sion)
			}

			next.ServeHTTP(w, r)
		})
	}
}
